iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
Modern Web

重寫一套比我還老的系統:21 歲的校園文字廣播系統系列 第 6 篇

Day 6|終於可以開始寫 Code 了……嗎?

  • 分享至 

  • xImage
  •  

鏘鏘鏘鏘鏘——鏘!

且說昨日,蘇某與蘇同學二人,對著一冊指指點點。

談的是名籍,論的是屬,最後甚至請出了一隻會說話的小黃鴨。

如今籍庫之制已定,權責既明,兵馬已齊。

也該是時候 —— 撰寫術文!

庫表既成,權責已定,此時不書,更待何時!

好好好,說得好!

此時不書,更待何時,先來建 repo。

建立後端 repo
建立前端 repo

一段好的旅程就應該先有個好的名字
就決定是你了!e-fdhs.v2!

聽起來像隨便亂取的

隨便啦,看得懂就好。

先來看看後端要選什麼?應該還是 FastAPI 吧。

我也這麼覺得,非同步架構給我的負擔應該不會太大。

ok 加油。

先幫你把 conda 搞定。
接下來交給你了。

建立 conda 環境

現在的 AI 已經比你那時好用多了,加油!

先裝個 FastAPI,
寫個基本架構再說。

快速的寫了一個基本架構並在 Agent 下了 prompt

(下 prompt [規劃模式])

撰寫database.py
利用db.dbml建立model.py
並利用model.py建立service.py

Agent 詢問要用 SQLAlchemy 2 async 還是要用 sync

這邊問了要用 SQLAlchemy 2 async 還是要用 sync。
那這邊既然都用了 FastAPI 了,那就用 async 吧。

歐?那你知道如果用 sync 會發生什麼事嗎?

大概就是……同步查詢會卡住?

async 比較快?

等等,先不要急著說 async 比較快。

這兩件事不是同一回事。

總之先讓模型繼續工作吧。

Agent 詢問要由什麼方式建立/更新

它又問說 ORM 模型完成後,資料表結構由什麼方式建立/更新?
這兩個有差嗎?

嗯,好問題,我知道差在哪裡,但是細節我不太熟,我問一下 GPT 好了。

ok 好,這兩種方式處理的是不太一樣的問題。

假設今天我們在 SQLAlchemy 裡寫好了這張表:

class Account(Base):
    __tablename__ = "accounts"

    id = mapped_column(UUID, primary_key=True)
    account = mapped_column(String)

如果使用 create_all(),SQLAlchemy 會去資料庫看:

「喔?accounts 不存在?」

那就幫你建一張。

那不是很好嗎?

如果你打算從此以後再也不改 Schema 的話,確實很好。

怎麼可能。

對,所以問題來了。

假設過了一個禮拜,我突然覺得:

不行,我需要知道這個帳號叫什麼名字。

於是 Model 變成:

class Account(Base):
    __tablename__ = "accounts"

    id = mapped_column(UUID, primary_key=True)
    account = mapped_column(String)
    display_name = mapped_column(String, nullable=True)

然後再跑一次 create_all()。

你猜會怎樣?

什麼都不會做?

y。

它看到 accounts 已經存在,就不會自動把現有的表改成你現在 Model 的樣子。

……那我自己下 ALTER TABLE?

可以。

然後下次再改一次 Schema,你再寫一次。

再下次,再寫一次。

等哪天 Production 的資料庫跟你本機長得不一樣,你就可以開始考古:

「這張表到底經歷過什麼?」

這就是 Alembic 要處理的問題。

它不是單純看著現在的 Model,然後把資料庫「啪!」一下變成一模一樣。

我們會留下每一次 Schema 變更的 Migration:

001_create_accounts
        ↓
002_add_display_name
        ↓
003_add_group_id

每一次 Schema 的變更,都會留下一份 Migration。

喔,聽起來有點像 Git?

可以這麼說,資料庫 Schema 專用的 Git。

就決定是你了!Alembic。

Agent 詢問建立或更新帳號時,service.py 如何處理密碼?

它又問說建立或更新帳號時,service.py 如何處理密碼?

這邊的話我幫你選。
選僅接受 password_hash,
這樣你之後要自己寫驗證層會比較方便。

Agent 詢問被帳號或子群組引用的群組/職位/角色,要如何刪除?

Agent 問被帳號或子群組引用的群組/職位/角色,要如何刪除?

這題你自己選。

嗯……
第二個應該先排除吧。
今天如果我把「資訊股長」這個職位刪掉,總不能順便把全校資訊股長的帳號一起揚了。

聽起來挺刺激的 hhh。

軟刪除好像可以,但我們現在真的需要嗎?

我是覺得應該還不用。

那就第一個?
還有人用,就不給刪。

而且你還記得昨天 DBML 裡寫了什麼嗎?

Foreign Key?

嗯哼,既然一個帳號還指向某個職位,那這個職位就不能憑空消失。

不然你的 position_id 的 FK 要指去哪?

總之,先選 拒絕刪除。

真的想刪,就先把引用它的帳號處理乾淨。

Agent 詢問查詢帳號有效權限時,要採用哪個合併規則?

Agent 又詢問查詢帳號有效權限時,要採用哪個合併規則?

它問題真多。
選一,先合併後再套 deny。

為什麼不選第二個「例外完全覆蓋」?

那會變成只要出現 Override,原本 Position 和 Role 的權限就全部不算。
但我們昨天設計 Override 的目的,是要修正例外,不是整包權限砍掉重算。

Codex 規劃完成詢問是否可以執行

我看看,沒問題了,執行吧。

Codex 執行完成

e-fdhs\backend
├── alembic
│   ├── versions
│   │   └── 20260920_0001_initial_authorization_schema.py
│   ├── env.py
│   └── script.py.mako
├── database
│   ├── __init__.py
│   ├── database.py
│   ├── db.dbml
│   ├── model.py
│   └── service.py
├── pytest-cache-files-b7rs_707
├── tests
│   ├── conftest.py
│   └── test_authorization_services.py
├── .env
├── .gitignore
├── alembic.ini
├── example.env
├── main.py
└── requirements.txt

嗯,看起來沒什麼大問題。
今天應該可以收工了。

你就打算這樣收工了?

差不多吧。

差不多?
你是不是還欠我一個問題?

嗯,好像有那麼一回事,啥問題來著?

剛剛你問我如果用 sync 會怎樣。
我說「async 比較快」,然後你叫我先不要急著下結論。
所以 async 到底快在哪?

喔。
對齁。

那我出個功課吧。
回去想想看。

time.sleep(5)

跟

await asyncio.sleep(5)

這兩段程式,有什麼不一樣?

不就一個同步、一個非同步?

對啊。
但它們不是都要等五秒嗎?

既然都是五秒——
那 async 到底快在哪裡?

明天我們再來看看。


上一篇
Day 5|今天的小黃鴨有點高級,會問會答,還不用消耗 token
下一篇
Day 7|都是五秒,Async 到底快在哪裡?
系列文
重寫一套比我還老的系統:21 歲的校園文字廣播系統 共 20 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言